﻿namespace EFS.Common.Async
{
	/// <summary>
	/// Async process 
	/// </summary>
	public class AsyncProcess : System.IDisposable
	{
		#region Composition

		// Root
		private EFS.Common.Async.AsyncRoot root = null;

		// Complete Event
		private System.Threading.ManualResetEvent waitEvent = null;

		// Completed flag
		private System.Boolean completed = false;

		// Process work
		private AsyncRoot.ProcessWork processWork = null;

		// Call back when completed
		private AsyncRoot.AsyncRootProcessCompleted asyncProcessCompleted = null;

		// Call back when unhandled exception
		private AsyncProcess.AsyncProcessUnahndledException asyncProcessUnhandledException = null;

		// lock 
		private System.Object syncAccess = new System.Object();

		// exception
		private System.Exception exception = null;

		/// <summary>
		/// Time out choices
		/// </summary>
		public enum Timeout
		{
			Never = -1,
			Short = 3000,
			Mediun = 6000,
			Long = 12000
		}

		#endregion

		#region Delegates

		/// <summary>
		/// Async method to be call 
		/// </summary>
		internal delegate void AsyncProcessUnahndledException(System.Exception exception);

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		internal AsyncProcess(AsyncRoot root, AsyncRoot.ProcessWork processWork, AsyncRoot.AsyncRootProcessCompleted asyncProcessCompleted, AsyncProcess.AsyncProcessUnahndledException asyncProcessUnhandledException)
		{
			// root & work can not be null
			EFS.Common.Verify.Reference(root, "root");
			EFS.Common.Verify.Reference(processWork, "processWork");
			EFS.Common.Verify.Reference(asyncProcessUnhandledException, "asyncProcessUnhandledException");

			// make wait event
			waitEvent = new System.Threading.ManualResetEvent(false);

			// keep 
			this.root = root;
			this.processWork = processWork;
			this.asyncProcessCompleted = asyncProcessCompleted;
			this.asyncProcessUnhandledException = asyncProcessUnhandledException;
		}

		#endregion

		#region Usage

		/// <summary>
		/// Wait complete
		/// </summary>
		public void Throw()
		{
			// lock-it so one thread only can reach inside
			// ~ commented for performance ~  			EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check completed
				if (!completed)
				{
					throw new System.InvalidOperationException("Process not yet Completed");
				}

				// check fr exceptions
				if (Exception != null)
				{
					throw new System.Exception("Async Process Exception", Exception);
				}
			}
		}

		/// <summary>
		/// Wait complete
		/// </summary>
		public void WaitComplete(Timeout timeout = Timeout.Never)
		{
			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check completed
				if (completed)
				{
					return;
				}
			}

			// wait to cpmplete
			if (!waitEvent.WaitOne((System.Int32)timeout))
			{
				throw new System.TimeoutException();
			}
		}

		/// <summary>
		/// State return our root
		/// </summary>
		public AsyncRoot Root { get { return root; } }

		/// <summary>
		/// Get wait handle
		/// </summary>
		public System.Threading.WaitHandle WaitHandle
		{
			get { return waitEvent; }
		}

		/// <summary>
		/// Completed 
		/// </summary>
		public System.Boolean Completed
		{
			get { return completed; }
		}

		/// <summary>
		/// Exception
		/// </summary>
		public System.Exception Exception
		{
			get { return exception; }
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Run asink process
		/// </summary>
		internal void RunAsyncProcess()
		{
			try
			{
				// call asynk method
				processWork();

				// set completetd fine
				SetCompleted(null);
			}
			catch (System.Exception ex0)
			{
				// set completed with error
				SetCompleted(ex0);
			}
			finally
			{
				// set event 
				waitEvent.Set();
			}

			try
			{
				// call back if requested
				asyncProcessCompleted(this);
			}

			catch (System.Exception ex1)
			{
#if !SILVERLIGHT				
				// must handle exception here
				EFS.EventLog.Log.LogException(ex1, root);
#endif
				try
				{
					// call back to unhandled exception
					asyncProcessUnhandledException(ex1);
				}
#if !SILVERLIGHT
				catch (System.Exception ex2)
				{
					// must handle exception here
					EFS.EventLog.Log.LogException(ex2, root);
#else
				catch 
				{	
#endif
				}
			}
		}

		/// <summary>
		/// Set completed
		/// </summary>
		/// <param name="exception"></param>
		/// <param name="completedSynchronously"></param>
		private void SetCompleted(System.Exception exception)
		{
			// loack
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check
				if (completed)
				{
					throw new System.InvalidOperationException("Process already Completed");
				}

				// keep exception
				this.exception = exception;

				// set
				completed = true;
			}
		}

		#endregion

		#region Destructor

		/// <summary>
		/// Dispose
		/// </summary>
		public void Dispose()
		{
			try
			{
				// clean event
				if (waitEvent != null)
				{
					waitEvent.Close();
					waitEvent = null;
				}

				// clean references
				root = null;
				processWork = null;
				asyncProcessCompleted = null;
			}
#if !SILVERLIGHT
			catch (System.Exception ex)
			{

				EFS.EventLog.Log.LogException(ex, null);
#else
			catch
			{
#endif
			}
			finally { }
		}

		/// <summary>
		/// Destructor
		/// </summary>
		~AsyncProcess()
		{
			Dispose();
		}

		#endregion
	}
}